package es.ucm.fdi.bdt;

import java.io.Serializable;
import java.net.MalformedURLException;
import java.util.Arrays;
import java.util.Set;

import de.uniba.wiai.lspi.chord.data.URL;
import de.uniba.wiai.lspi.chord.service.Chord;
import de.uniba.wiai.lspi.chord.service.PropertiesLoader;
import de.uniba.wiai.lspi.chord.service.ServiceException;
import de.uniba.wiai.lspi.chord.service.impl.ChordImpl;

public class ChordOverlayNetworkManager {
	
	private Chord _chord;

	public ChordOverlayNetworkManager(boolean isMaster, String host, int port) {
		super();
		PropertiesLoader.loadPropertyFile();
		if (isMaster)
			createChordNetwork(port);
		else
			joinChordNetwork(host, port);
	}

	public boolean register(RegistrationInfo info) throws ServiceException {
		StringKey key = new StringKey(info.getUserName());
		if (nodeAlreadyExists(key)){
			return false;
		}

		_chord.insert(key, info);

		StringKey sk = new StringKey("usuarios");
		_chord.insert(sk, info.getUserName());		
		System.out.println("Insert usuarios: " + info.getUserName());		
		System.out.println("Retrieve usuarios: " + _chord.retrieve(sk).toString());		
		
		//System.out.println("usuarios:"+Arrays.toString(_chord.retrieve(sk).toArray(new Serializable[]{})));	
		return true;
	}
	
	public void mostrarUsuariosActivos(){
		StringKey sk = new StringKey("usuarios");
				
		try {
			Set<Serializable> usuarioActivos = _chord.retrieve(sk);			
			if (!usuarioActivos.isEmpty()){
				System.out.println("usuarios:"+
					Arrays.toString(usuarioActivos.toArray(new Serializable[]{})));		
			} else {
				System.out.println("no hay usuarios activos.");
			}
		} catch (ServiceException e) {
			e.printStackTrace();
		}		
		
	}
	
	public void enviarMensaje(Message msg){
		StringKey sk = new StringKey("hash_de_msg");
		try {
			_chord.insert(sk, msg);
		} catch (ServiceException e) {
			e.printStackTrace();
		}		
	}
	
	public Set<Serializable> recuperarMensaje(){
		StringKey sk = new StringKey("hash_de_msg");
		try {
			return _chord.retrieve(sk);
		} catch (ServiceException e) {
			e.printStackTrace();
		}
		return null;		
	}

	public boolean updateRegistrationInformation(RegistrationInfo newInfo)
			throws ServiceException {
		StringKey key = new StringKey(newInfo.getUserName());
		Set<Serializable> nodes = _chord.retrieve(key);
		if (nodes.isEmpty())
			return false;

		RegistrationInfo registrationInfo = (RegistrationInfo) nodes.toArray()[0];
		_chord.remove(key, registrationInfo);
		_chord.insert(key, newInfo);
		return true;
	}

	public void unregister(RegistrationInfo info) throws ServiceException {
		StringKey key = new StringKey(info.getUserName());
		_chord.remove(key, info);
	}

	public RegistrationInfo lookup(String username) throws ServiceException {
		StringKey key = new StringKey(username);
		Set<Serializable> nodes = _chord.retrieve(key);
		if (nodes.isEmpty())
			return null;

		return (RegistrationInfo) nodes.toArray()[0];
	}

	private void createChordNetwork(int port) {
		String protocol = URL.KNOWN_PROTOCOLS.get(URL.SOCKET_PROTOCOL);

		String localUrlString;
		URL localUrl = null;
		localUrlString = createUrl(protocol, ChatClient.ipExt(), port);
		try {
			localUrl = new URL(localUrlString);
		} catch (MalformedURLException e) {
			throw new RuntimeException("Incorrect Url: " + localUrlString, e);
		}

		_chord = new ChordImpl();
		try {
			_chord.create(localUrl);
		} catch (ServiceException exception) {
			throw new RuntimeException("Could not create DHT ! ", exception);
		}
	}

	private void joinChordNetwork(String bootstrapHost, int bootstrapPort) {
		String protocol = URL.KNOWN_PROTOCOLS.get(URL.SOCKET_PROTOCOL);

		int localPort = ChatClient.CLIENT_PORT;

		String localUrlStr;
		URL localUrl = null;
		
		localUrlStr = createUrl(protocol, ChatClient.ipExt(), localPort);
		
		try {
			localUrl = new URL(localUrlStr);
		} catch (MalformedURLException e) {
			throw new RuntimeException("Incorrect Url: " + localUrlStr, e);
		}

		String bootstrapUrlStr = createUrl(protocol, bootstrapHost, bootstrapPort);
		System.out.println("BootstrapHost:"+ bootstrapUrlStr);
		URL bootstrapUrl = null;
		try {
			bootstrapUrl = new URL(bootstrapUrlStr);
		} catch (MalformedURLException e) {
			throw new RuntimeException("Incorrect Url: " + bootstrapUrlStr, e);
		}

		_chord = new ChordImpl();
		try {
			_chord.join(localUrl, bootstrapUrl);
		} catch (ServiceException e) {
			throw new RuntimeException("Could not join DHT!", e);
		}
	}

	private boolean nodeAlreadyExists(StringKey key) throws ServiceException {
		return !_chord.retrieve(key).isEmpty();
	}

	/*private int getPort() throws IOException {
		ServerSocket server = new ServerSocket(0);
		int localPort = server.getLocalPort();
		server.close();
		return localPort;
	}*/

	private String createUrl(String protocol, String host, int port) {
		return protocol + "://" + host + ':' + port + '/';
	}
}
